package com.wmx.yuanyuan.controller;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * logback 日志框架，动态切换日志输出级别
 * 无论 logback.xml 文件是否存在，都不影响本类的运行，因为只要引入了依赖，则默认 root 日志输出级别为 info.
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/8/14 12:51
 */
@RestController
@SuppressWarnings("all")
public class LogbackController implements InitializingBean, DisposableBean {

    private static final Logger log = (Logger) LoggerFactory.getLogger(LogbackController.class);

    /**
     * 实现 InitializingBean 接口
     * 程序启动，当 Bean 初始化时，自动执行 afterPropertiesSet 方法。
     * 类似 @PostConstruct 注解。
     * InitializingBean 是在每一个bean 初始化完成后调用，多实例的情况下每初始化一次就调用一次。
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("实例初始化完成！");
    }

    /**
     * 由希望在实例销毁时释放资源的 bean 实现 DisposableBean 接口。
     * 类似 @PreDestroy 注解，当实例销毁时，自动执行 destroy 方法。
     *
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        log.info("实例销毁...");
    }

    /**
     * 打印日志测试
     * http://localhost:8080/logback/logs
     */
    @GetMapping("logback/logs")
    public void logs() {
        log.trace("hello, I am trace!");
        log.debug("hello, I am debug!");
        log.info("hello, I am info!");
        log.warn("hello, I am {}!", "warn");
        log.error("hello, I am {}!", "error");
    }

    /**
     * logback 动态切换指定包或者类的日志输出级别，立即生效.
     * http:localhost:8080/logback/setLevel
     * http:localhost:8080/logback/setLevel?level=DEBUG
     * http:localhost:8080/logback/setLevel?level=DEBUG&clazz=com.wmx
     *
     * @param level ：日志级别，可选值有：ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF，默认为 debug。不区分大小写.
     * @param clazz ：指定的包或者类路径，为空时默认设置全局(root)日志级别。路径不存在时，照样会设置这个路径，不会影响全局级别，这一点与 log4j2 不同.
     *              比如 org.springframework。
     * @return map  ：返回指定路径的当前日志级别，root 表示全局级别
     */
    @GetMapping(value = "/logback/setLevel")
    public Map<String, Object> setLevel(String level, String clazz) {
        Map<String, Object> dataMap = new HashMap<>(8);
        dataMap.put("code", 200);
        Logger logger;
        try {
            // 返回正在使用的 ILoggerFactory 实例.
            LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
            if (clazz == null || "".equals(clazz.trim())) {
                //Logger getLogger(final String name) ：根据包路径或类路径获取日志记录器，"ROOT" 表示根记录器
                logger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
                dataMap.put("clazz", Logger.ROOT_LOGGER_NAME);
            } else {
                //Logger getLogger(final String name) ：根据包路径或类路径获取日志记录器，"ROOT" 表示根记录器
                //路径不存在也没有关系，照样会返回它的日志记录器，然后设置输出级别.
                logger = loggerContext.getLogger(clazz);
                dataMap.put("clazz", clazz);
            }
            //Level toLevel(String sArg)：转换为日志级别，如果转换失败，则默认为 debug 级别.
            //synchronized void setLevel(Level newLevel)：为日志记录器设置日志输出级别，立即生效.
            logger.setLevel(Level.toLevel(level));

            //获取日志记录器的日志输出级别.
            String logLevel = logger.getLevel().toString();
            dataMap.put("level", logLevel);
        } catch (Exception e) {
            dataMap.put("code", 500);
            dataMap.put("msg", e.getMessage());
            log.error(e.getMessage(), e);
        }
        return dataMap;
    }

    /**
     * 获取指定包或者类路径的日志输出级别，默认获取 root(根)全局日志输出级别
     * http:localhost:8080/logback/getLevel
     * http:localhost:8080/logback/getLevel?clazz=
     *
     * @param clazz ：包或类路径，比如 org.springframework。
     *              1、即使记录不存在也关系，因为不存在也能设置输出级别
     *              2、无论路径存在与否，只要没有在配置文件或者程序中单独指定其输出级别，则其日志记录器对应的日志输出级别(Level)为 null.
     *              3、无论有没有 Logback.xml 文件，只要引入了依赖，root 全局输出级别默认就是 info.
     * @return
     */
    @GetMapping(value = "/logback/getLevel")
    public Map<String, Object> getLevel(String clazz) {
        Map<String, Object> dataMap = new HashMap<>(8);
        dataMap.put("code", 200);
        Logger logger;
        try {
            // 返回正在使用的 ILoggerFactory 实例.
            LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
            if (clazz == null || "".equals(clazz.trim())) {
                //Logger getLogger(final String name) ：根据包路径或类路径获取日志记录器，"ROOT" 表示根记录器
                logger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
                dataMap.put("clazz", Logger.ROOT_LOGGER_NAME);
            } else {
                //Logger getLogger(final String name) ：根据包路径或类路径获取日志记录器，"ROOT" 表示根记录器
                //路径不存在也没有关系，照样会返回它的日志记录器.
                logger = loggerContext.getLogger(clazz);
                dataMap.put("clazz", clazz);
            }
            //获取日志记录器的日志输出级别，可能为 null.
            Level loggerLevel = logger.getLevel();
            if (loggerLevel != null) {
                dataMap.put("level", loggerLevel.toString());
            } else {
                dataMap.put("msg", "此日志记录器没有对应的日志输出级别！");
            }
        } catch (Exception e) {
            dataMap.put("code", 500);
            dataMap.put("msg", e.getMessage());
            log.error(e.getMessage(), e);
        }
        return dataMap;
    }

}
